package othello;

import org.apache.wicket.ResourceReference;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.image.Image;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.panel.FeedbackPanel;

import org.apache.wicket.ajax.AjaxEventBehavior;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.AbstractAjaxTimerBehavior;

import org.apache.wicket.model.Model;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;

import org.apache.wicket.util.time.Duration;

import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;

import othello.core.Board;
import othello.core.BadHandException;
import othello.core.OthelloPlayer;
import othello.core.RandomPutPlayer;
import othello.core.NextHandInfo;
import othello.core.OthelloUtil;

public class Othello extends WebPage{
    //オセロの親コンポーネント(AjaxでListViewの更新を行う為に必要)
    private WebMarkupContainer parent;

    //進行状況や結果を表示するラベル
    private Label result;
    
    //エラーを伝えるFeedbackPanel
    private FeedbackPanel feedback;

    //オセロのマスの画像
    private ResourceReference whiteStone = new ResourceReference(Othello.class, "img/white.gif");
    private ResourceReference blackStone = new ResourceReference(Othello.class, "img/black.gif");
    private ResourceReference empty = new ResourceReference(Othello.class, "img/empty.gif");

    //オセロのボードを表す変数
    private Board board;
    //現在のプレイヤーの石の色
    private int currentColor = Board.WHITE;
    //現在のプレイヤー
    private OthelloPlayer currentPlayer;
    
    //ユーザープレイヤー
    private UserPlayer player;
    //小瀬のプレイヤーの配列
    private OthelloPlayer[] players;
    //現在のプレイヤーのインデックス
    private int currentPlayerIndex = 0;

    //createRowListViewで使う列を表す変数
    private int row = 0;
    //createColumnListViewで使う列を表す変数
    private int column = 0;

    public Othello(){
        //コンポーネントの初期化
        feedback = new FeedbackPanel("feedback");
        feedback.setOutputMarkupId(true);
        result = new Label("result", "あなたの番です");
        result.setOutputMarkupId(true);
        
        ListView rowList = createRowListView();
        
        parent = new WebMarkupContainer("parent");
        parent.setOutputMarkupId(true);

        //コンポーネントの追加
        parent.add(rowList);
        parent.add(feedback);
        parent.add(result);
        add(parent);

        //オセロの初期化
        board = new Board();
        player = new UserPlayer();
        player.setColor(currentColor);
        
        OthelloPlayer computer = new RandomPutPlayer();
        computer.setColor(-currentColor);
        
        players = new OthelloPlayer[2];
        players[0] = player;
        players[1] = computer;
        currentPlayer = player;
        addTimer();
    }

    //行方向のリストビューを作成
    private ListView createRowListView(){
        ListView<Integer> rowList = new ListView<Integer>("rowList", Arrays.asList(new Integer[8])){
            @Override
            protected void populateItem(ListItem<Integer> item){
                //列方向のリストビューを追加する
                item.add(createColumnListView(row));
                row = (row + 1) % 8;
            }
        };
        
        rowList.setOutputMarkupId(true);
        return rowList;
    }
    
    //列方向のリストビューを作成
    private ListView createColumnListView(final int row){
        ListView<Integer> listView = new ListView<Integer>("columnList", Arrays.asList(new Integer[8])){
            @Override
            protected void populateItem(ListItem<Integer> item){
                int stone = board.getCell(column + 1, row + 1);
                Image image;
                //マスに置かれている石の色によって画像を変える
                if(stone == Board.WHITE)
                    image = new Image("image", whiteStone);
                else if(stone == Board.BLACK)
                    image = new Image("image", blackStone);
                else
                    image = new Image("image", empty);

                //imageに対するAjaxイベントの追加
                addAjaxEvent(image, column, row);
                
                //Ajaxで更新可能にする
                image.setOutputMarkupId(true);
                
                item.add(image);

                column = (column + 1) % 8;
            }
        };
        
        listView.setOutputMarkupId(true);
        return listView;
    }
    
    //imageにAjaxイベントの追加
    private void addAjaxEvent(Image image, final int column, final int row){
        image.add(new AjaxEventBehavior("onMouseDown"){
            @Override
            protected void onEvent(final AjaxRequestTarget target){
                //現在のプレイヤがユーザプレイヤで次の手がまだ決まってなければ次の手を決める
                if(player.getColor() == currentColor && player.nextHand((Board)board.clone()) == null){
                    NextHandInfo nextHand = new NextHandInfo(column + 1, row + 1);
                    player.setNextHand(nextHand);
                }
            }
        });
    }
    
    //タイマーイベントの追加、オセロの状態の変更
    private void addTimer(){
        //100ミリ秒ごとにイベントを発生させる
        add(new AbstractAjaxTimerBehavior(Duration.milliseconds(100)){
            @Override
            protected void onTimer(AjaxRequestTarget target){
                //現在のプレイヤーの次の手を取得する
                NextHandInfo nextHand = currentPlayer.nextHand((Board)board.clone());
                if(nextHand == null){
                    //まだ決まってなければreturn;
                    return;
                }
                
                try{
                    //ボードに石を置く
                    board.putStone(currentColor, nextHand.getX(), nextHand.getY());
                    target.addComponent(parent);
                }catch(BadHandException e){
                    //置けない場所ならエラーメッセージ表示
                    error("そこには置けません");
                    target.addComponent(feedback);
                    
                    //現在のプレイヤーがユーザープレイヤーなら次の手初期化
                    if(player.getColor() == currentColor)
                        player.setNextHand(null);
                    
                    return;
                }

                //プレイヤーを交代する
                shiftPlayer();
                //現在のプレイヤーがユーザープレイヤーなら次の手初期化
                if(player.getColor() == currentColor)
                    player.setNextHand(null);
                
                //石の数が64になったら勝敗を表示
                if(board.getStoneCount() == 64){
                    viewOthelloResult();
                    stop();
                }

                //結果を更新
                target.addComponent(result);
            }
        });
    }
    
    //プレイヤーを交代する
    private void shiftPlayer(){
        //置く場所が無ければ交代は行わない
        if(OthelloUtil.getCanPutList(board, -currentColor).size() == 0)
            return;

        //プレイヤーを交代する
        currentPlayerIndex = (currentPlayerIndex + 1) % 2;
        currentPlayer = players[currentPlayerIndex];
        currentColor = -currentColor;

        //現在のプレイヤーによってメッセージ切り替え
        if(player.getColor() == currentColor)
            result.setDefaultModelObject("あなたの番です");
        else
            result.setDefaultModelObject("コンピュータの番です");
    }
    
    //勝敗を表示するメソッド
    private void viewOthelloResult(){
        int playerStoneCount = board.getStoneCount(player.getColor());
        if(playerStoneCount > 32){
            result.setDefaultModelObject("あなたの勝ちです");
        }else if(playerStoneCount == 32){
            result.setDefaultModelObject("引き分けです");
        }else{
            result.setDefaultModelObject("あなたの負けです");
        }
    }
}
